# coding:utf-8
import numpy as np
import pandas as pd
# 柯里化一个print
printn = lambda *x: print(*x, sep='\n')
'''
轴的理解，axis=0,从左到右，每列执行。
axis=1，从上到下，每行执行。
'''


# Series
def testSeries():
    obj = pd.Series([4, 7, -5, 3])
    printn('Series1:', obj, 'Series1的值:', obj.values, 'Series1的索引:', obj.index)
    # 指定索引
    obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
    printn('Series2:', obj2, 'Series2的索引:', obj2.index, '索引选取值:', obj2['d'])
    # 修改数据
    obj2['d'] = 6
    # 花式索引，列表作为索引
    printn("花式索引:", obj2[['c', 'a', 'd']])
    # 使用numpy函数,会保留索引值的链接
    printn(
        "bool函数:", obj2[obj2 > 0], '和标量相乘:', obj2 * 2, '计算e的指数:', np.exp(obj2))
    # Series看成一个定长的有序字典，可以使用需要字典参数的函数中
    print('in函数判断索引:', 'b' in obj2)
    # 使用字典创建Series
    sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
    obj3 = pd.Series(sdata)
    printn('字典常见的Series:', obj3)
    # 如果只传入一个字典，则Series的索引就是该字典的键。也可以传入排好序的字典的键改变顺序
    states = ['California', 'Ohio', 'Oregon', 'Texas']
    # 此例中'California'在字典中没有，故而在Series中用NAN表示；同样'Utah'在索引states中没有，从结果中去除
    obj4 = pd.Series(sdata, index=states)
    printn('指定键生成Series:', obj4)
    # 检测缺失数据,顶级方法，或使用对象方法:isnull或notnull
    # printn("确实数据检测空:",pd.isnull(obj4),"确实数据检测非空:",pd.notnull(obj4))
    printn("缺失数据检测空:", obj4.isnull(), "缺失数据检测非空:", obj4.notnull())
    printn('===asof函数:====', obj4.asof(['Ohio']))  # 返回最近一个不是NA值的数据
    # 最重要的一个功能------根据运算的索引标签对齐数据
    printn("相加时对其索引:", obj3 + obj4)
    # Series对象本身及其索引都有⼀个name属性
    obj4.name = 'population'
    obj4.index.name = 'state'
    print("本身的name和index的name:", obj4.name, obj4.index.name)
    # Series的索引可以通过赋值的方式修改
    obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
    printn('就地更改索引后的Series:', obj)
# testSeries()


# DataFrame
'''
可以输入给DataFrame构造器的数据：二维ndarray；由数组、列表或元组组成的字典；
NumPy的结构化/记录数组；由Series组成的字典；由字典组成的字典；字典或Series的列表；
由列表或元组组成的列表；另一个DataFrame；NumPy的MaskedArray
'''


def testDataframe():
    # 最常用的方法，直接传入一个由等长列表或Numpy数组组成的字典
    data = {
        'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
        'year': [2000, 2001, 2002, 2001, 2002, 2003],
        'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]
    }
    frame = pd.DataFrame(data)  # 自动加上索引
    printn('frame的前5行:', frame.head())  # 显示前5行
    # 若指定了列序列，DataFrame会根据顺序排列
    frame1 = pd.DataFrame(data, columns=['year', 'state', 'pop'])
    printn("指定列序列后:", frame1)
    # 传入的列在数据中找不到，结果产生NAN值
    frame2 = pd.DataFrame(
        data,
        columns=['year', 'state', 'pop', 'debt'],
        index=['one', 'two', 'three', 'four', 'five', 'six'])
    printn("找不到数据时填入NAN:", frame2, "列的列表:", frame2.columns)

    # 索引方式返回的列只是视图而已，故修改都会反馈到原DataFrame
    # 字典方式访问，获得一个Series,name属性值就是列名
    printn('列索引获得一个Series:', frame2['state'])
    # 也可用属性方式访问，但列名必须是一个合格的python变量名，否则出错
    printn('属性方式访问:', frame2.year)
    # 行的访问，使用loc,也是一个Series，name属性值是行索引
    printn('loc方法进行行索引:', frame2.loc['three'])
    # 赋值的方式改变列,值可以是标量，也可以是一个序列、numpy数组或合适长度的迭代器。
    # 创建新列只能用字典方式，不能用属性方式
    frame2['debt'] = range(6)
    printn('迭代器赋值:', frame2)
    # 如果赋值的是一个Series，则精确匹配DataFrame的索引，所有的空位都将填上缺失值
    val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
    frame2['debt'] = val
    printn('Series赋值给DataFrame:', frame2)
    # 删除列：del。添加一个bool列作为例子,state是否为Ohio的值赋值给eastern列
    frame2['eastern'] = frame2.state == 'Ohio'
    printn('bool值赋值给列:', frame2)
    del frame2['eastern']
    printn('del删除列后的列列表:', frame2.columns)

    # 另一种常见的数据形式：嵌套字典
    pop = {
        'Nevada': {2001: 2.4,
                   2002: 2.9},
        'Ohio': {2000: 1.5,
                 2001: 1.7,
                 2002: 3.6}
    }
    # pandas会将外层字典的键作为列，内层字典的键作为行索引
    frame3 = pd.DataFrame(pop)
    printn("内行，外列形成DataFrame:", frame3)
    # 类似numpy的方法，转置
    printn('frame3的转置:', frame3.T)
    # 内层字典的键会被合并、以一定顺序形成最终的索引。如果明确指定了索引，则不会
    frametest = pd.DataFrame(pop, index=[2001, 2002, 2003])
    printn('嵌套字典，指定索引形成的DataFrame:', frametest)

    # Series组成的字典，差不多用法
    pdata = {'Ohio': frame3['Ohio'][:-1], 'Nevada': frame3['Nevada'][:2]}
    printn('Series组成的字典:', pd.DataFrame(pdata))

    # 如果设置了DataFrame的index和columns的name属性，则这些信息也会被显示
    frame3.index.name = 'year'
    frame3.columns.name = 'state'
    printn('指定了index和columns的name属性后:', frame3)
    # 和Series一样，values属性也会以二维ndarray的形式返回DataFrame中的数据
    printn('DataFrame的values属性:', frame3.values)
    # # 如果DataFrame各列的数据类型不同，则值数组的dtype会选用能兼容所有列的数据类型
    printn('值类型兼容:', frame2.values.dtype)
# testDataframe()


# 索引对象
'''
index对象是不可变的，故而其可在多个数据结构之间安全共享
方法和属性：append--连接另一个Index对象，产生一个新的Index；difference---差集；intersection--交集
union--并集，isin--指示各值是否都包含在参数集合中，生成布尔型数组；delete--删除i处的元素，得到新的Index
drop--删除传入的值，得到新的Index；insert--插入i处得到新的Index；is_monotonic--当各元素均大于等于某一个元素时，返回True
is_unique--当index没有重复值时，返回True；unique--计算Index中唯一值的数组
'''


def testindex():
    # 索引对象负责管理轴标签和其他元数据（⽐如轴名称等）
    obj = pd.Series(range(3), index=['a', 'b', 'c'])
    index = obj.index
    printn('索引:', index, '索引切片:', index[1:])
    # Index对象共享
    # 顶级函数，pd.Index构建一个index
    lables = pd.Index(np.arange(3))
    printn("构建的Index:", lables)
    obj2 = pd.Series([1.5, -2.5, 0], index=lables)
    printn(
        '指定共享的Index后:', obj2, 'DataFrame的Index是否就是共享的那个:',
        obj2.index is lables)
    # pandas的Index可以包含重复的标签
    dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
    printn("重复标签的Index:", dup_labels)
# testindex()


# -------------------------基本功能--------------------------
'''
reindex函数的参数：target--用作索引的新序列(可以是Index实例，也可是序列型数据)；method--插值方式；
fill_value--需要引入缺失值时使用的替代值；limit--前向或后向填充时的最大填充量；
tolerance--向前或向后填充时，填充不准确匹配相对额最大间距(绝对值距离)
level--在MultiIndex的指定级别上匹配简单索引，否则选取其子集
copy--默认为True，无论如何都复制；如果为False，则新旧相等就不复制
'''


def testreindex():
    # 重新索引reindex：创建一个新对象，它的数据符合新的索引
    obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
    printn('旧数据:', obj)
    # 用该Series的reindex将会根据新索引重排，某个索引对应的值不存在就引入缺失值
    obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
    printn('reindex重排后:', obj2)
    # 对于时间序列这样的有序数列，重新索引时可能需要做一些插值。method选项可以做到
    # 例如ffill实现前向值填充
    obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
    obj3_1 = obj3.reindex(range(6), method='ffill')
    printn('旧数据2:', obj3, 'reindex重排后2:', obj3_1)
    # 借助DataFrame，reindex可以修改(行)索引和列。只传递一个序列时，会重新索引结果的行。
    frame = pd.DataFrame(
        np.arange(9).reshape((3, 3)),
        index=['a', 'c', 'd'],
        columns=['Ohio', 'Texas', 'California'])
    printn('原DataFrame:', frame)
    frame2 = frame.reindex(['a', 'b', 'c', 'd'])
    printn('reindex重排后:', frame2)
    # 列可以用columns关键字重新索引,即列的重排要用columns关键字
    states = ['Texas', 'Utah', 'California']
    frame2_2 = frame.reindex(columns=states)
    printn('列的重排:', frame2_2)
# testreindex()


# 丢弃指定轴上的项
def testdiscard():
    # drop方法返回一个在指定轴上删除了指定值的新对象
    obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
    new_obj = obj.drop('c')
    printn("删除了c索引所在行:", new_obj)
    # 传入序列用于删除多行
    new_obj2 = obj.drop(['d', 'c'])
    printn("使用序列删除多行:", new_obj2)

    # 对于DataFrame，可以删除任意轴上的索引值。
    data = pd.DataFrame(
        np.arange(16).reshape((4, 4)),
        index=['Ohio', 'Colorado', 'Utah', 'NewYork'],
        columns=['one', 'two', 'three', 'four'])
    # 用标签序列，从行标签删除，不指定轴，默认在行上操作
    new_data = data.drop(['Colorado', 'Ohio'])
    printn('原DF:', data, '删除多行后:', new_data)
    # 通过指定axis=1或axis='columns'可以删除列,传入序列删除多列
    new_data2 = data.drop('two', axis=1)
    new_data3 = data.drop(['two', 'four'], axis='columns')
    printn('删除一列:', new_data2, '删除多列:', new_data3)
    # 若要原地修改数据，可以使用inplace参数
    obj.drop('c', inplace=True)
    printn('inplace为True后:', obj)
# testdiscard()


# --------------索引、选取和过滤-------------
def testsec():
    obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
    # 索引
    printn('标签索引和位置索引分别是:', obj['b'], obj[1])
    # 切片
    printn('切片:', obj[2:4])
    # 列表索引
    printn('列表进行索引(标签和位置均可):', obj[['b', 'a', 'd']], obj[[1, 3]])
    # 不能使用列表索引没有的标签，比如obj[['b','e']],'e'标签就没有，可以使用reindex索引
    printn("使用没有的标签进行索引:", obj.reindex(['b', 'e']))
    # 布尔筛选
    printn("bool索引:", obj[obj < 2])

    # 利用标签的切片运算要注意，其包含末端
    printn('包含末端的切片:', obj['b':'c'])

    # 用切片可以对Series的相应部分进行设置，赋值标量表示所有切片内容赋同一值
    obj['b':'c'] = 5
    printn('切片赋值:', obj)

    # DataFrame
    # 用一个值或序列对DataFrame进行索引其实就是获取一个或多个列
    data = pd.DataFrame(
        np.arange(16).reshape((4, 4)),
        index=['Ohio', 'Colorado', 'Utah', 'NewYork'],
        columns=['one', 'two', 'three', 'four'])
    # 标签或列表索引
    printn("标签索引:", data['two'], '列表索引:', data[['three', 'one']])
    # 切片
    printn('切片:', data[:2])  # 行切片
    # 布尔数组筛选
    printn('bool数组索引:', data[data['three'] > 5])
    # 布尔型DataFrame索引
    data[data < 5] = 0  # 数据里小于5的置0
    printn('bool型DataFrame例子:', data)

    # 对DataFrame的行的标签进行索引，引入了特殊的标签运算符loc和iloc,多行、多列选取要用列表
    # 单行和列表的多列索引--loc借助标签
    printn('一行多列选取:', data.loc['Colorado', ['two', 'three']])
    # iloc配合整数索引
    printn(
        'iloc的整数索引，一行多列:', data.iloc[2, [3, 0, 1]], '单行:', data.iloc[2],
        '多行多列:', data.iloc[[1, 2], [3, 0, 1]])
    # loc和iloc也可使用1个或多个标签的切片
    printn('loc使用标签切片:', data.loc[:'Utah', 'two'])
    printn('iloc使用切片:', data.iloc[:, :3][data.three > 5])
    # 可以对一行数据使用字典进行赋值
    x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
    printn('原始x:', x)
    x.iloc[1] = {'x': 9, 'y': 99}
    printn('字典修改后的x:', x)
    # 如果想要获取pandas里的某一个标量值，可以使用at或iat,速度比loc和iloc快
    printn("某一位置的标量值:", x.at[2, 'y'])

    # pandas通过iloc和loc对Series或DataFrame赋值时，会对齐所有的轴
    df = pd.DataFrame(
        np.random.randn(8, 4),
        index=pd.date_range('1/1/2000', periods=8),
        columns=list('ABCD'))
    printn('原始df:', df)
    # 比如讲A、B列交换，df.loc[:,['B','A']] = df[['A','B']]是没有反应的，因为loc会先和后面的索引对齐
    # 正确写法，是使用原始数据raw data
    df.loc[:, ['B', 'A']] = df[['A', 'B']].to_numpy()
    printn('修改后的df:', df)

    # 如果轴索引是整数，数据选取总会使用标签，为了更准确，请使用loc(标签)和iloc(整数)
    ser = pd.Series(np.arange(3.))
    printn('借助iloc来使用整数索引:', ser.iloc[-1])  # 不能使用ser[-1]
    # 非整数索引，勉强可以使用整数索引
    ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
    printn('非整数索引勉强可以直接用整数索引:', ser2[-1])
# testsec()


# ---------------算术运算和数组对齐-----------------
def testcal():
    # 不同索引对象相加时，结果的索引是索引对的并集;就像在索引标签上进行自动外连接
    s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
    s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
    printn('不同索引对象相加:', s1 + s2)  # 自动的数据对齐，在不重叠的索引处引入NA值
    # DataFrame则会在行和列上都进行并集，自动数据对齐
    df1 = pd.DataFrame(
        np.arange(9.).reshape((3, 3)),
        columns=list('bcd'),
        index=['Ohio', 'Texas', 'Colorado'])
    df2 = pd.DataFrame(
        np.arange(12.).reshape((4, 3)),
        columns=list('bde'),
        index=['Utah', 'Ohio', 'Texas', 'Oregon'])
    printn('不同索引DataFrame相加:', df1 + df2)
    # 如果两个DataFrame没有共同的行列索引，则结果全是NA
    df1 = pd.DataFrame({'A': [1, 2]})
    df2 = pd.DataFrame({'B': [3, 4]})
    printn('没有共⽤的列或⾏标签:', df1 + df2)
# testcal()


# ------------------------算术方法中填充值-----------------
'''
Series和DataFrame的算术方法，每个方法都有一个副本，以字母r开头，翻转参数。比如1/df1和df1.rdiv(1)是一样的
add,radd--(+);sub,rsub--(-);div,rdiv--(/);floordiv,rfloordiv--(底除//);mul,rmul--(*);pow,rpow--(指数**)
'''


def testcalfill():
    # 当计算时，可能希望当一个对象中某个轴标签在另一个对象中找不到时填充一个值
    df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))
    df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)), columns=list('abcde'))
    df2.loc[1, 'b'] = np.nan
    printn('无重叠位置产生NAN:', df1 + df2)  # 这就产生NA值
    # 使用add方法,df1加上df2,数据缺失的位置用fill_value参与计算
    df3 = df1.add(df2, fill_value=0)
    printn('add函数的fill_value参数使用:', df3)
    # 算数方法中，带r和不带r的函数是一对，r表示参数翻转
    printn('原函数:', 1 / df1, '翻转:', df1.rdiv(1), '第二个也表示1/df1')
    # 与此类似，Series或DataFrame重新索引时，也可以指定一个填充值。
    df1_1 = df1.reindex(columns=df2.columns, fill_value=0)
    printn('DataFrame重新索引时，通过fill_value指定填充值:', df1_1)
# testcalfill()


# -------------DataFrame和Series之间的运算-------------------------
def testSD():
    # 计算二维数组与其某行之间的差
    arr = np.arange(12.).reshape((3, 4))
    # 当arr减去arr[0]时，每一行都会执行这个操作，这个叫广播
    printn('减去一行', arr - arr[0])
    # DataFrame和Series计算
    frame = pd.DataFrame(
        np.arange(12.).reshape((4, 3)),
        columns=list('bde'),
        index=['Utah', 'Ohio', 'Texas', 'Oregon'])
    series = frame.iloc[0]
    # 默认情况，DataFrame和Series之间的算术运算会将Series的索引匹配到DataFrame的列，然后沿着行一直向下广播
    printn("原DataFrame:", frame, '索引匹配列后向下广播:', frame - series)
    # 如果某个索引值在DataFrame的列或Series的索引中找不到，则参与运算的两个对象就会被重新索引以形成列的并集,非共同索引填NA值
    series2 = pd.Series(range(3), index=['b', 'e', 'f'])
    printn("series2:", series2)
    printn('运算后形成列的并集:', frame + series2)

    # 如果匹配行且在列上广播，必须使用算术运算方法
    series3 = frame['d']
    ccal = frame.sub(series3, axis=0)  # 指定axis=index或axis=0
    printn('匹配行且在列上广播:', ccal)
# testSD()


# -------------------函数应用和映射---------------------
# numpy的ufuncs(元素级数组方法)也可用于操作pandas对象
# 许多常见的数组统计功能都被实现成DataFrame的方法(如sum和mean)，无需使用apply方法
def testfunc():
    frame = pd.DataFrame(
        np.random.randn(4, 3),
        columns=list('bde'),
        index=['Utah', 'Ohio', 'Texas', 'Oregon'])
    printn('原DataFrame:', frame, "绝对值", np.abs(frame))
    # 另一个常见的操作，将函数应用到由各列或行所形成的一维数组。可使用apply方法,返回新的DataFrame/Series
    f = lambda x: x.max() - x.min()  # 此处x是某行或某列
    # 这里的f计算一个Series的最大值和最小值的差，在frame的每列都执行了一次。结果是一个Series，使用frame的列做索引
    fapply = frame.apply(f)
    printn('每列执行一次:', fapply)
    # 如果传递axis='columns'或axis=1到apply，这个函数会在每行执行
    fapply2 = frame.apply(f, axis=1)
    printn('每行执行一次:', fapply2)

    # 传递给apply的函数不是必须返回一个标量，还可以返回由多个值组成的Series
    def f(x):
        return pd.Series([x.min(), x.max()], index=['min', 'max'])

    fapply3 = frame.apply(f)
    printn('apply返回Series时:', fapply3)
    # 元素级的Python函数也是可以用的。假如得到frame中各个浮点值的格式化字符串，可使用applymap
    formatd = lambda x: '%.2f' % x  # 使用%分隔格式和数据
    fapply4 = frame.applymap(formatd)
    printn('元素级python方法:', fapply4)

    # Series有一个元素级的函数，map
    fapply5 = frame['e'].map(formatd)
    printn('Series的元素级map:', fapply5)
# testfunc()


# -----------------排序和排名---------------
def testsort():
    # 要对行或列索引进行排序，可使用sort_index方法，返回一个已排序的新对象
    # Series
    obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
    new_obj = obj.sort_index()
    printn('Index排序后的Series:', new_obj)

    # DataFrame
    frame = pd.DataFrame(
        np.arange(8).reshape((2, 4)),
        index=['three', 'one'],
        columns=['d', 'a', 'b', 'c'])
    # 行索引排序
    printn('行索引排序后:', frame.sort_index())
    # 列索引排序,传入axis=1
    printn('列索引排序后:', frame.sort_index(axis=1))
    # 数据默认是升序排列，也可以降序排列
    printn('列索引排序降序:', frame.sort_index(axis=1, ascending=False))

    # 若要按值对Series排序，可使用sort_values方法
    obj = pd.Series([4, 7, -3, 2])
    printn('Series的值排序:', obj.sort_values())
    # 排序时，任何缺失值默认都会被放到Series的末尾
    obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
    printn('Series排序NAN值放最后:', obj.sort_values())

    # 对DataFrame的值排序，希望根据一个或多个列中的值进行排序。
    # 将一个或多个列的名字传给sort_values的by选项
    frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
    printn('DataFrame对值排序需用by指定列标签:', frame.sort_values(by='b'))
    # 要根据多个列进⾏排序，传⼊名称的列表即可，按主次排序，前一个相同时才排后一个
    printn('多列进行排序传入列表给by参数:', frame.sort_values(by=['a', 'b']))

    # rank方法：通过“为各组分配一个平均排名”的方式破坏平级关系的
    '''
    method参数：'average'--默认：在相等分组中，为各个值分配平均排名；
    'min'--使用整个分组的最小排名;'max'--使用整个分组的最大排名
    'first'--按值在原始数据中的出现顺序分配排名
    'dense'--类似于'min'方法，但是排名总是在组间增加1，而不是组中相同的元素数。
    '''
    # rank排名是从1开始到有效数据数量，若有相同的，默认分配平均排名，如两个相同元素要占用第4，第5位，则分配4.5给这两个元素
    obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
    printn("默认排名:", obj.rank())
    # 可以根据值在原数据中出现的顺序给出排名：所以数据中的两个7，第一个rank为6，第二个为7
    printn('按原始数据中出现的顺序排名:', obj.rank(method='first'))
    # 也可以按降序排列，max表示使用最大排名，比如两个元素占据6，7名，则都分配为第7名
    printn(obj.rank(ascending=False, method='max'))
    # DataFrame的行或者列排名
    frame = pd.DataFrame(
        {'a': [0, 1, 0, 1],
         'b': [4.3, 7, -3, 2],
         'c': [-2, 5, 8, -2.5]})
    printn('DataFrame的排名:', frame.rank(axis=1))  # axis=1或columns表示从上到下，每行执行
# testsort()


# --------------带有重复标签的轴索引------------------
def testdupindex():
    obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
    # 索引的is_unique属性可以告诉你是否唯一
    print('索引是否唯一:', obj.index.is_unique)
    # 如某个索引对应多个值，则返回一个Series，对应单个值的返回一个标量
    printn('索引对多值返回Series:', obj['a'], obj['c'])

    # DataFrame也是一样
    df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
    printn('索引对多值返回Series:', df.loc['b'])
# testdupindex()


# -------------------汇总和计算描述统计-------------------
'''
pandas对象的数学和统计方法都属于约简和汇总统计，用于从Series里提取单个值，或从DataFrame里提取Series
它们都是基于没有缺失数据的假设构建的。
'''


def testtotal():
    '''
    约简方法的选项：axis--约简的轴，行是0，列是1；skipna---排除缺失值，默认True；
    level---如果轴是层次化索引的(即MultiIndex)，则根据level分组约简
    '''
    df = pd.DataFrame(
        [[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]],
        index=['a', 'b', 'c', 'd'],
        columns=['one', 'two'])
    # 调用DataFrame的sum方法，返回一个含有列的和的Series
    printn('约简和:', df.sum())  # 默认axis=0，从左到右，每列操作
    # 传入axis='columns'或axis=1对行进行求和运算
    printn("行的约简和", df.sum(axis=1))
    # 上述参数中NA值会被自动排除，除非整个切片(指行或者列)都是NA。可通过skipna选项禁用该功能
    printn("行的均值(统计NA):", df.mean(axis=1, skipna=False))

    # 一些方法返回的是间接统计，如idxmax和idxmin，最大值，最小值的索引
    printn("最大值的索引:", df.idxmax())

    # 另一些方法则是累计型的
    printn("累计和:", df.cumsum())

    # 还有一些方法既不是约简型也不是累计型。如describe，一次性产生多个汇总统计
    printn("describe说明:", df.describe())

    # 对非数值型数据的describe
    obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
    printn("非数值型describe:", obj.describe())
    """
    描述 与汇总统计
    count--非NA值的数量；describe--针对Series或各DataFrame列计算汇总统计；
    min、max--计算最小值和最大值；argmin、argmax--计算能够获取最小值和最大值的索引位置(整数)；
    idxmin、idxmax--计算能够获取最小值和最大值的索引值；quantile--计算样本的分位数(0-1);
    sum--值的总和；mean--值的平均数；median--值的算术中位数(50%分位数)；mad--根据平均值计算平均绝对离差；
    var--方差；std--标准差；skew--样本值的偏度(三阶矩)；kurt--样本值的峰度(四阶矩)；
    cumsum--样本值的累计和；cummin、cummax--样本值的累计最小值和累计最大值；
    cumprod--样本值的累计积；diff--计算一阶差分(对时间序列很有用)；pct_change---计算百分数变化
    """
# testtotal()


# ----------相关系数与协方差------
# 有些汇总统计是通过参数对计算出来的
# 安装pandas-datareader包，获取Yahoo!Finance的股票价格和成交量 pip install pandas-datareader
def testcorr():
    import pandas_datareader.data as web
    import datetime as dt
    all_data = {
        ticker: web.DataReader(ticker, 'yahoo', '2019-01-01', dt.date.today())
        for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']
    }
    price = pd.DataFrame(
        {ticker: data['Adj Close']
         for ticker, data in all_data.items()})
    volume = pd.DataFrame(
        {ticker: data['Volume']
         for ticker, data in all_data.items()})
    # print(price.head())
    returns = price.pct_change()
    printn('returns的后5行:', returns.tail())
    # Series的corr方法用于计算两个Series中重叠的、非NA的、按索引对齐的值的相关系数，与此类似，cov计算协方差
    printn("相关性:", returns['MSFT'].corr(returns['IBM']))
    printn("协方差:", returns['MSFT'].cov(returns['IBM']))
    # 也可使用属性的方法
    printn("相关性:", returns.MSFT.corr(returns.IBM))
    # 另一方面DataFrame的corr和cov方法将以DataFrame的形式分别返回完成的相关系数或协方差矩阵
    printn("all相关性:", returns.corr())
    printn("all协方差:", returns.cov())
    # 利用DataFrame的corrwith方法，你可以计算其列或行跟另一个Series或DataFrame之间的相关系数
    # 传入一个Series将会返回一个相关系数值Sereis
    printn('returns和returns.IBM的相关性:', returns.corrwith(returns.IBM))
    # 传入一个DataFrame则会计算按列名匹配的相关系数。计算百分比变化于成交量的相关系数
    printn('两个DataFrame的相关性', returns.corrwith(volume))
# testcorr()


# ---------唯一值、值计数和成员资格
'''
isin--计算一个表示“Series各值是否包含于传入的值序列中”的布尔型数组
match--计算一个数组中的各值到另一个不同值数组的整数索引；对于数据对其和谅解类型的操作十分有用
unique--计算Series中的唯一值数组，按发现的顺序返回
value_counts--返回一个Series，其索引为唯一值，其值为频率，按计数值降序排列
'''


def testsome():
    # unique函数，得到Series中的唯一值数组,未排序
    obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
    uniques = obj.unique()
    printn('unique提取唯一值:', uniques)
    # 对结果进行排序(unique.sort())
    printn('结果排序:', uniques.sort())

    # value_counts计算一个Series中各值出现的频率,结果Series是按值频率降序排列
    printn("出现频率:", obj.value_counts())
    # value_counts还有一个顶级pandas方法，用于任何数组或序列
    printn('顶级函数统计:', pd.value_counts(obj.values, sort=False))

    # isin用于判断矢量化集合的成员资格，可用于过滤Series中或DataFrame列中数据的子集
    mask = obj.isin(['b', 'c'])
    printn('isin判断结果:', mask, 'bool过滤:', obj[mask])

    # 与isin类似的是Index.get_indexer方法，它可以给你一个索引数组，从可能包含重复值的数组到另一个不同值的数组
    to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
    unique_vals = pd.Series(['c', 'b', 'a'])
    indextest = pd.Index(unique_vals)
    # to_match里的元素在无重复值unique_vals中的位置索引
    printn('在无重复值中的索引:', indextest.get_indexer(to_match))

    # 希望得到DataFrame中多个相关列的一张柱状图
    data = pd.DataFrame(
        {
            'Qu1': [1, 3, 4, 3, 4],
            'Qu2': [2, 3, 1, 2, 3],
            'Qu3': [1, 5, 2, 4, 4]
        })
    # 将pandas.value_counts传给该DataFrame的apply函数
    # 结果中行标签是所有列的唯一值。后面的频率值是每个列中这些值的相应计数。没有的填0
    result = data.apply(pd.value_counts).fillna(0)
    printn('每列的唯一值统计:', result)


testsome()
